# !/usr/bin/env python
# -*-coding:utf-8 -*-

"""
# File       : flow_save_utils.py
# Time       ：2023/10/30 22:27
# Author     ：Y-aong
# version    ：python 3.10
# Description：流程保存工具类
"""
from typing import List

from apis.orderlines.models import Task
from apis.orderlines.utils.parallel_node_utils import ParallelNodeUtils
from public.base_model import session_scope
from public.logger import logger


class FlowSaveUtils:
    def __init__(self, process_id: str):
        self.process_id = process_id
        from public.utils.mongo_utils import MongoDBUtil
        self.mongo = MongoDBUtil('flow_data')
        self.parallel_index = 0

    @property
    def graph_data(self):
        filter_ = {'process_id': self.process_id}
        flow_data = self.mongo.collection.find_one(filter_)
        if '_id' in flow_data:
            flow_data.pop('_id')
        return flow_data.get('graphData')

    @property
    def nodes(self):
        return self.graph_data.get('nodes')

    @property
    def edges(self):
        return self.graph_data.get('edges')

    @staticmethod
    def get_start_node(task_nodes: List[dict]):
        """获取开始节点"""
        for node in task_nodes:
            if node.get('type') == 'start-node':
                return node
        raise ValueError('该节点没有开始节点')

    @staticmethod
    def get_next_node(task_id: str, task_nodes: List[dict]):
        """获取next任务节点"""
        for task_node in task_nodes:
            if task_node.get('prev_id') == task_id:
                return task_node
            elif task_node.get('prev_id') and task_id in task_node.get('prev_id'):
                return task_node
        return None

    @staticmethod
    def get_parallel_next_id(task_id: str, task_nodes: List[dict], ):
        """获取并行节点"""
        for task_node in task_nodes:
            if task_node.get('prev_id') == task_id:
                return task_node.get('task_id')
            elif task_node.get('prev_id') and task_id in task_node.get('prev_id'):
                return task_node.get('task_id')

        raise ValueError('并行节点没有next id')

    @staticmethod
    def get_process_node(task_id: str, task_nodes: List[dict]):
        """获取流程控制后面的任务节点"""
        process_nodes = list()
        for task_node in task_nodes:
            if task_node.get('prev_id') == task_id:
                process_nodes.append(task_node)
        return process_nodes

    @staticmethod
    def get_group_start_node(children: List[str], task_nodes: List[dict]):
        """获取任务组中的开始节点"""
        for node in task_nodes:
            if node.get('task_id') in children and not node.get('prev_id'):
                return node
        logger.info('任务组中为空,没有其他任务')
        return None

    @staticmethod
    def check_in_sorted_task(task_id: str, sorted_task_nodes: List[dict]):
        """检测任务点位是否在"""
        for node in sorted_task_nodes:
            if node.get('task_id') == task_id:
                return True
        return False

    def generate_node_relation(self):
        """生成节点的prev_id和next_id"""
        task_nodes = list()
        for node in self.nodes:
            task_id = node.get('id')
            task_name = node.get('text').get('value')
            prev_id, next_id = list(), list()
            for edge in self.edges:
                if edge.get('sourceNodeId') == task_id:
                    next_id.append(edge.get('targetNodeId'))
                if edge.get('targetNodeId') == task_id:
                    prev_id.append(edge.get('sourceNodeId'))
            item = {
                'task_id': task_id,
                'task_name': task_name,
                'prev_id': ','.join(prev_id) if prev_id else None,
                'next_id': ','.join(next_id) if next_id else None,
                'type': node.get('type')
            }
            if node.get('type') == 'group-node':
                item.setdefault('children', node.get('children'))
            task_nodes.append(item)
        return task_nodes

    @staticmethod
    def get_current_node(task_id: str, task_nodes: list):
        for node in task_nodes:
            if node.get('task_id') == task_id:
                return node
        return None

    def get_group_task(self, children: list, task_nodes: list, sorted_task_nodes: list, prev_id: str, task_id: str):
        """获取任务组中的任务顺序"""
        new_children = list()
        group_start_node = self.get_group_start_node(children, task_nodes)
        if not group_start_node:
            # 任务组中的最后一个节点直接插入
            node = self.get_current_node(task_id, task_nodes)
            new_children.append(task_id)
            if not self.is_exist(node, sorted_task_nodes):
                sorted_task_nodes.append(node)
            return new_children
        else:
            # 先将任务组开始节点插入
            if group_start_node.get('task_id') not in new_children and group_start_node.get('task_id') in children:
                new_children.append(group_start_node.get('task_id'))

            group_start_node['prev_id'] = prev_id
            if not self.is_exist(group_start_node, sorted_task_nodes):
                sorted_task_nodes.append(group_start_node)
            group_start_id = group_start_node.get('task_id')

        for _ in task_nodes:
            next_node = self.get_next_node(group_start_id, task_nodes)
            if next_node and next_node.get('task_id') not in new_children:
                if next_node.get('task_id') in children:
                    new_children.append(next_node.get('task_id'))
                if not self.is_exist(next_node, sorted_task_nodes):
                    sorted_task_nodes.append(next_node)
                group_start_id = next_node.get('next_id')
        return new_children

    @staticmethod
    def is_exist(item: dict, sort_task: list):
        """检查node是否存在"""
        for temp in sort_task:
            if temp.get('task_id') == item.get('task_id'):
                return True
        return False

    def generate_task_node(self):
        """生成任务节点"""
        sorted_task_nodes = list()
        task_nodes = self.generate_node_relation()
        parallel_utils = ParallelNodeUtils(self.process_id, task_nodes)

        start_node = self.get_start_node(task_nodes)
        sorted_task_nodes.append(start_node)
        current_task_id = start_node.get('task_id')

        while len(sorted_task_nodes) != task_nodes:
            next_task_node = self.get_next_node(current_task_id, task_nodes)
            if not next_task_node:
                logger.info(f'没有下一个节点开始退出,current_task_id::{current_task_id}')
                break
            task_type = next_task_node.get('type')
            # 流程控制特殊处理
            if next_task_node and task_type == 'process-control-node':
                if not self.is_exist(next_task_node, sorted_task_nodes):
                    sorted_task_nodes.append(next_task_node)
                current_task_id = next_task_node.get('task_id')
                process_nodes = self.get_process_node(current_task_id, task_nodes)
                for process_node in process_nodes:
                    if process_node.get('type') == 'group-node' and process_node.get('children'):
                        children = process_node.get('children')
                        for node in task_nodes:
                            task_id = node.get('task_id')
                            if not node.get('prev_id') and not node.get('next_id') and task_id in children:
                                node['prev_id'] = process_node.get('prev_id')
                                node['next_id'] = process_node.get('next_id')
                                if not self.is_exist(node, sorted_task_nodes):
                                    sorted_task_nodes.append(node)
                            elif task_id in children and not self.check_in_sorted_task(task_id, sorted_task_nodes):
                                # 任务组内任务顺序
                                prev_id = next_task_node.get('prev_id')
                                data = self.get_group_task(children, task_nodes, sorted_task_nodes, prev_id, task_id)
                                process_node['children'] = data
                    if not self.is_exist(process_node, sorted_task_nodes):
                        sorted_task_nodes.append(process_node)
                    current_task_id = process_node.get('task_id')
            # 任务组需要特殊判断
            elif task_type == 'group-node' and next_task_node.get('children'):
                if not self.is_exist(next_task_node, sorted_task_nodes):
                    sorted_task_nodes.append(next_task_node)
                children = next_task_node.get('children')
                for node in task_nodes:
                    task_id = node.get('task_id')
                    if not node.get('prev_id') and not node.get('next_id') and task_id in children:
                        node['prev_id'] = next_task_node.get('prev_id')
                        node['next_id'] = next_task_node.get('next_id')
                        if not self.is_exist(node, sorted_task_nodes):
                            sorted_task_nodes.append(node)
                    elif task_id in children and not self.check_in_sorted_task(task_id, sorted_task_nodes):
                        # 任务组内任务顺序
                        prev_id = next_task_node.get('prev_id')
                        next_task_node['children'] = self.get_group_task(
                            children, task_nodes, sorted_task_nodes, prev_id, task_id)
                current_task_id = next_task_node.get('task_id')
            elif task_type == 'parallel-node':
                sorted_task_ids, parallel_task_ids = parallel_utils.handle_parallel_node(self.parallel_index)
                parallel_start_task_id, parallel_end_task_id = sorted_task_ids
                sorted_task_nodes.append({
                    'task_id': parallel_start_task_id,
                    'prev_id': current_task_id,
                    'next_id': parallel_end_task_id,
                    'task_name': '并行开始节点',
                })
                sorted_task_nodes.append({
                    'task_id': parallel_end_task_id,
                    'prev_id': parallel_start_task_id,
                    'next_id': self.get_parallel_next_id(parallel_end_task_id, task_nodes),
                    'task_name': '并行结束节点',
                })
                with session_scope() as session:
                    session.query(Task).filter(Task.task_id == parallel_end_task_id).update(
                        {'method_kwargs': {'parallel_task_ids': parallel_task_ids}}
                    )
                current_task_id = parallel_end_task_id
                self.parallel_index += 1
            else:
                if not self.is_exist(next_task_node, sorted_task_nodes):
                    sorted_task_nodes.append(next_task_node)
                current_task_id = next_task_node.get('task_id')

        return sorted_task_nodes
